#include "gtest/gtest.h"


#include "ComparableRocksdb.h"
#include "Rocksdb.h"
#include "OptionsArgs.h"

using namespace helper;
using namespace demo;


TEST(ComparableRocksdbTest, testMain){
  OptionsArgs oa;
  oa.open().create_if_missing = true;
  auto rdb = std::unique_ptr<IRocksdb<Slice, Slice>>(new Rocksdb("hello.db", oa));
  ASSERT_TRUE(rdb != nullptr);


  auto printSlice = [](const auto & k, const auto & v) -> bool {
      std::cout << "foreach:" << k.ToString() << ":" << v.ToString() << std::endl;
      return true;
  };

	{
		auto cdb = std::unique_ptr<ComparableRocksdb>(new ComparableRocksdb(*rdb, *::rocksdb::BytewiseComparator()));
		ASSERT_TRUE(cdb != nullptr);
		ASSERT_TRUE(cdb->open());

		cdb->put("key", "value");
    cdb->foreach(printSlice, Slice(), Slice("z"));
    cdb->del("key");

    ASSERT_TRUE(cdb->close());

	}
  {
		auto cdb = ComparableRocksdb::Open(*rdb, ::rocksdb::BytewiseComparator());
		ASSERT_TRUE(cdb != nullptr);
		cdb->put("key_for_Open", "value");

    cdb->foreach(printSlice, Slice(), Slice("z"));
    cdb->del("key_for_Open");
    ASSERT_TRUE(cdb->close());
  }

}


TEST(RocksdbTest, test){
  const std::string dbname("rocksdb.test.db");
  {// db instance   open-not-exist 
      std::unique_ptr<IDBInstance> rdb(Rocksdb::Open(dbname, OptionsArgs()));
      ASSERT_TRUE(rdb == nullptr);
  }

	{ // make new Rocksdb directly
    OptionsArgs optArgs;
    optArgs.database().create_if_missing = true;
    std::unique_ptr<Openable> rdb(new Rocksdb(dbname, optArgs));
		ASSERT_TRUE(rdb->open());
    rdb.reset();

    ASSERT_TRUE(Rocksdb::destroy(dbname, optArgs.open()));
	}

  {// db instance with options 
    OptionsArgs optArgs;
    optArgs.database().create_if_missing = true;

    std::unique_ptr<IDBInstance> rdb(Rocksdb::Open(dbname, optArgs));
    ASSERT_TRUE(rdb != nullptr);
    rdb.reset();

    ASSERT_TRUE(Rocksdb::destroy(dbname, optArgs.open()));
  }

  {// table interface 
    OptionsArgs optArgs;
    optArgs.database().create_if_missing = true;

    std::unique_ptr<ITable<Slice, Slice>> rdb(Rocksdb::Open(dbname, optArgs));
    const std::string key("hello");
    const std::string value("world");
    std::string valueIndb;
    {
      rdb->put(key, value);

      rdb->get(key, valueIndb);
      ASSERT_EQ(value, valueIndb);

      rdb->del(key);
    }

    { // with Options
      rdb->put(optArgs.write(), key, value);

      //TODO: special utest for it 
      auto * iter = rdb->range(Slice(), Slice());
      for(; iter->hasNext(); iter->next()){
        auto && [k, v] = iter->current();
        std::cout << "range:" << k.ToString() << ":" << v.ToString() << std::endl;
      }
      delete iter;

      //TODO: special utest for it 
      rdb->foreach([](const auto & k, const auto & v) -> bool {
        std::cout << "foreach:" << k.ToString() << ":" << v.ToString() << std::endl;
        return true;
      }, Slice(), Slice("z"));

      rdb->get(optArgs.read(), key, valueIndb);
      ASSERT_EQ(value, valueIndb);

      rdb->del(optArgs.write(), key);
    }

    rdb.reset();

    ASSERT_TRUE(Rocksdb::destroy(dbname, optArgs.open()));
  }



  { //TODO: ColumnFamilyRocksdb 
//      std::unique_ptr<IDB> rdb = ColumnFamilyRocksdb::Open(dbname, comparator);
//      auto && [tab, success] = rdb->tables().create(tabname);// ITable<Slice, Slice> & tab;
//      auto ok = rdb->tables().drop(tabname);
//      auto &&[tab, exist] = rdb->tables().get(tabName); // ITable<Slice, Slice> & tab;
//
//    //TODO: options in table scope
//    rdb->tables().create(tabName, open, read, write); // table scope options
  }
}

